#define EnvironmentSystem__Generate_Limbo
#define EnvironmentSystem__Generate_TheMaze

#if EnvironmentSystem__Generate_Limbo
	#define EnvironmentSystem__Save_Limbo
#endif

#if EnvironmentSystem__Generate_TheMaze
	#define EnvironmentSystem__Save_TheMaze
#endif

using System;

namespace SharpMud.MudLib.SAMPLE.Environment
{
	/// <summary>
	/// This class manages terrain and environment systems and events.
	/// </summary>
	public class EnvironmentSystem : Server.Systems.PulsarServerSystem
	{
		private AreaList							_AreaList;
		private System.Collections.ArrayList		_LoadedAreas;

        internal readonly log4net.ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodInfo.GetCurrentMethod().DeclaringType);

		public EnvironmentSystem()
		{
		}

		/// <summary>
		/// Initializes this environment system. Loads the area list, loads all areas, and initializes them.
		/// </summary>
		protected override void _Initialize()
		{
			base._Initialize ();
			this._LoadedAreas = new System.Collections.ArrayList();
			try
			{
				this._AreaList = AreaList.LoadAreaList(
					MudLibRoot.GetSubfileIntelligently(this.RootAreasFolder,"areas.xml"),this);
				
			}
			catch(System.IO.FileNotFoundException e)
			{
				log.Error("ERROR! Area list file not found!",e);
			}
			catch(System.IO.DirectoryNotFoundException e)
			{
				log.Error("ERROR! Directory not found (while loading area list)",e);
                log.Warn("Recursively creating directory '" + this.RootAreasFolder + "'");
			}
			if(this._AreaList == null)
			{
				log.Warn("No areas to load - the area list is strangely NULL.");
				this._AreaList = new AreaList(MudLibRoot.GetSubfileIntelligently(this.RootAreasFolder,@"areas.xml"),this);
			}
			if(this._AreaList.Areas.Length == 0)
			{
				log.Warn("No areas to load - the area list is EMPTY.");
			}
		
			this._LoadedAreas.AddRange( this.AreaList.LoadAllAreas() );

#if EnvironmentSystem__Generate_Limbo
			//Create Limbo. A permanent hard-coded administrative area.
			log.Info("Creating Limbo...");
			Area limbo = BuiltIn.Limbo.Create();
			foreach(Area a in _LoadedAreas)
			{
				if(a.Name.ToLower().Trim()==limbo.Name.ToLower().Trim())
				{
					log.Warn("Ignoring old 'Limbo'.");
					_LoadedAreas.Remove(a);
					break;
				}
			}
			this._LoadedAreas.Add(limbo);
			log.Info("Limbo now exists.");
#endif

#if EnvironmentSystem__Generate_TheMaze
			//Create TheMaze. A temporary hard-coded sample area.
			const int TheMaze_Width = 10;
			const int TheMaze_Height = 10;
			log.Info("Creating TheMaze...");
			Area theMaze = BuiltIn.TheMaze.Create(TheMaze_Width,TheMaze_Height);
			foreach(Area a in _LoadedAreas)
			{
				if(a.Name.ToLower().Trim()==theMaze.Name.ToLower().Trim())
				{
					log.Warn("Ignoring old 'TheMaze'.");
					_LoadedAreas.Remove(a);
					break;
				}
			}
			this._LoadedAreas.Add(theMaze);
			log.Info("TheMaze now exists.");
#endif

#if EnvironmentSystem__Generate_Limbo
#if EnvironmentSystem__Generate_TheMaze
			//Link Limbo and TheMaze together
			Room theVoid = limbo.GetRoom(new RoomID(BuiltIn.Limbo.TheVoid.Vnum));
			Room hell		= limbo.GetRoom(new RoomID(BuiltIn.Limbo.Hell.Vnum));
			Room mazeStart = theMaze.Rooms[0];
			Room mazeEnd = theMaze.Rooms[theMaze.Rooms.Length-1];

			theVoid.ExitCollection.Add(new SimpleDirectionalExit(ExitDirection.Down,mazeStart.RoomID));
			mazeEnd.ExitCollection.Add(new SimpleDirectionalExit(ExitDirection.Up,hell.RoomID));
#endif
#endif

			System.Xml.Serialization.XmlSerializer xs;
			System.IO.FileStream fs;

#if EnvironmentSystem__Generate_Limbo
#if EnvironmentSystem__Save_Limbo
			//Save limbo
			xs = new System.Xml.Serialization.XmlSerializer(
				typeof(Area), new Type[] {
											 typeof(Room),
											 typeof(SimpleDirectionalExit),
											 typeof(RoomID)
										 } );
			fs = new System.IO.FileStream(
				System.IO.Path.Combine(this.RootAreasFolder,"Limbo.AREA.xml"),System.IO.FileMode.Create);
			xs.Serialize(fs,limbo);
			fs.Close();
#endif
#endif

#if EnvironmentSystem__Generate_TheMaze
#if EnvironmentSystem__Save_TheMaze
			//Save themaze
			xs = new System.Xml.Serialization.XmlSerializer(
				typeof(Area), new Type[] {
											 typeof(Room),
											 typeof(SimpleDirectionalExit),
											 typeof(RoomID)
										 } );
			fs = new System.IO.FileStream(
				System.IO.Path.Combine(this.RootAreasFolder,"TheMaze.AREA.xml"),System.IO.FileMode.Create);
			xs.Serialize(fs,theMaze);
			fs.Close();
#endif
#endif

			this.InitializeAllAreas();

			log.Info("Initialization successful!");
		}

		private void RecursivelyCreateDirectory(string directory)
		{
			if(System.IO.Directory.Exists(directory)) return;

			if(!System.IO.Directory.Exists(System.IO.Directory.GetParent(directory).FullName))
			{
				RecursivelyCreateDirectory(System.IO.Directory.GetParent(directory).FullName);
			}
			log.Warn("Creating directory: \"" + directory + "\"");
			System.IO.Directory.CreateDirectory(directory);
		}


		/// <summary>
		/// The list of areas that constitute the environment
		/// </summary>
		public AreaList AreaList
		{
			get
			{
				return _AreaList;
			}
		}

		/// <summary>
		/// Gets the root path of the folder that environmental data is kept in
		/// </summary>
		public string RootEnvironmentFolder
		{
			get
			{
				return MudLibRoot.GetSubfolderIntelligently(this.Host.RootDataFolder,"environment");
			}
		}
		/// <summary>
		/// Gets the root path of the folder that areas and area lists are kept in
		/// </summary>
		public string RootAreasFolder
		{
			get
			{
				return MudLibRoot.GetSubfolderIntelligently(RootEnvironmentFolder,"areas");
			}
		}
		/// <summary>
		/// This method searches through areas for the room that matches the given RoomID,
		/// unless the RoomID already explicityly points to room
		/// </summary>
		/// <param name="roomID">The ID of the room to get</param>
		/// <returns>Returns the appropriate room for the given RoomID, if found. Otherwise, returns Null.</returns>
		public Room GetRoom(RoomID roomID)
		{
			Room r;
			if(roomID.Room!=null)
			{
				return roomID.Room;
			}
			else
			{
				foreach(Area a in this._LoadedAreas)
				{
					r =	a.GetRoom(roomID);
					if(r!=null) return r;
				}
			}
			return null;
		}

		/// <summary>
		/// Initializes all of the loaded areas
		/// </summary>
		protected void InitializeAllAreas()
		{
			log.Info("Initializing AREAs...");
			foreach(Area a in this._LoadedAreas)
			{
				log.InfoFormat("Initializing Area '{0}'",a.Name);
				a.Initialize(this);
			}
			log.Info("DONE initializing AREAs.");
		}

        protected override void _Dispose()
        {
            foreach (Area a in this._LoadedAreas)
            {
                a.Dispose();
            }
            base._Dispose();
        }
	}
}
